;rotating 3d starfield
;code: www.sensenstahl.com
;written for the A86
;history:
;5.feb.2o1o raw version 351b [no rotation]
;5.feb.2o1o optimized here and there --> 307b [no rotation]
;6.feb.2o1o worked on "beauty" (color-fading) 363b [no rotation]
;13.feb.2o1o z-rotation! 439b (= after doing that in tp7.0 it finally works as hardcode here
;14.feb.2o1o optimizations here and there --> 391b
;15.feb.2o1o key-functions + new colors --> 510b :)

;esc = esc
;other keys change stuff

data segment
;-------- data ------------
s_x dw 2048 dup ? ;x
s_y dw 2048 dup ? ;y
s_z dw 2048 dup ? ;z
s_c dw 2048 dup ? ;color

sn_x dw 2048 dup ? ;x after rotation
sn_y dw 2048 dup ? ;y

nx dw ? ;single x for pixel
ny dw ? ;single y for pixel
nz dw ? ;single z for pixel

calcx dw ? ;calculated 2d x
calcy dw ? ;calculated 2d y

color db ?

rndvalue dw ? ;result coming from calling get_rnd and range of values!

;--------------------------

code segment
org 0100h

;----- set 13h 320x200x256 vga ----
mov ax,0013h
int 10h

push 0a000h
pop es

;cx = 0
;rval= 255

looping05:  inc byte rval
            inc byte gval
            call pal
            cmp byte rval,63
            jb  looping05

looping06: dec byte gval
           dec byte rval
           inc byte bval
           call pal
           cmp byte rval,45
           ja looping06

looping07: inc byte bval
           call pal
           cmp byte bval,63
           jb looping07

looping08: call pal
           jnz looping08


mov cx,2048
init_stars:
call create_star
loop init_stars

weiter:
;---draw stars---
mov cx,2048  ;number of visible stars
malen:
mov ax,cx
shl ax,1   ;mul 2
push ax
pop si     ;always correct offset

mov byte color,0 ;erase old star
call calc2d ;make 2d values + pixel

dec word [offset s_z+si]       ;get value for calculation
jnz okee                     ;nz not zero
call create_star             ;if zero create new star :)

okee:
cmp byte [offset s_c+si],255 ;color management
je go_on
inc byte [offset s_c+si]
go_on:
mov byte color, byte [offset s_c+si]


;rotate around z axis
;thanks to the source of the 256b intro "Squirmer" by Gergely Kutenich
;where i found out to handle these parts @ fpu
fninit ;init fpu WITHOUT fwait
;                    st(0)
fldpi               ;pi
fimul word grad     ;pi*grad
fidiv word for_rad  ;pi*grad/180 (grad in rad!)
fsincos             ;cos sin

fld st(1)           ;sin cos sin
fimul word s_y+si   ;sin*y cos sin
fld st(1)           ;cos sin*y cos sin
fimul word s_x+si   ;cos*x sin*y cos sin
fsub st(0),st(1)    ;cos*x-sin*y sin*y cos sin
fist word sn_x+si

fxch st(0),st(2)    ;cos sin*y cos*x-sin*y sin
fimul word s_y+si   ;cos*y sin*y cos*x-sin*y sin
fld st(3)           ;sin cos*y sin*y cos*x-sin*y sin
fimul word s_x+si   ;sin*x cos*y sin*y cos*x-sin*y sin
fadd st(0),st(1)    ;sin*x+cos*y sin*y cos*x-sin*y sin
fist word sn_y+si
;###end rotation

call calc2d

loop malen
;-----------------

cmp byte key,2 ;only rotate at 2
jb weidda
cmp byte key,3 ;and at 3
ja weidda
inc word grad  ;degrees for rotation
cmp grad,361   ;reached 360°?
jb weidda
mov word grad,1
weidda:

;wait for vsync
mov dx, 3dah  ;port on vga to check retrace
vsync1:
in al,dx      ;get byte
test al,8     ;test bit 8 (and); bit 1 for horizontal retrace
jnz vsync1    ;1 = in retrace; loop until 0
vsync2:
in al,dx
test al,8
jz vsync2     ;0 = not in retrace; loop until 1

mov ah,01h     ;00 readkey; 01 keypressed
int 16h        ;bios int
jz ding

mov ah,00h     ;00 readkey; get key ; same as xor ah,ah
int 16h        ;bios int;grab keyboardbuffer

cmp al,27 ;esc
je ende ;STOP! HAMMERTIME!

;0=no rotation + small stars <----.
;1=no rotation + big stars        |
;2=rotation + big stars           |
;3=rotation + small stars         |
;4 -------------------------------'

inc byte key
cmp byte key,2
jb ding

mov cx,32000  ;clrscr
xor di,di
xor ax,ax
rep stosw

cmp byte key,4
jb ding

mov byte key,0

ding:
jmp weiter     ;go on

ende:
mov ax, 0003h
int 10h ; set 03h = textmode 80x25
ret ;back to dos

;########################## procedures/functions ##########################

;############create random number within range
;in = rndvalue: range; out = rndvalue = value
get_rnd:
in al,40h
add ah,al
in al,40h
xor dx,dx
mov bx, word rndvalue ;range
idiv bx     ;nun rest in dx
mov word rndvalue, dx ;save rnd
ret

;############create x,y,z for star[cx]
create_star:
push cx
pop si     ;correct offset (cx*2)
shl si,1   ;mul 2
mov byte [offset s_c+si],0
sternx:;-------create x-values--------
mov word rndvalue,160
call get_rnd
sub rndvalue,80
mov word [offset s_x+si],word rndvalue ;save
sterny:;-------create y-values--------
mov word rndvalue,100
call get_rnd
sub rndvalue,50
mov word [offset s_y+si],word rndvalue ;save
sternz:;-------create z-values--------
mov word rndvalue,100
call get_rnd
add rndvalue,100
mov word [offset s_z+si],word rndvalue ;save
ret


;############generate 2d x,y out of 3d x,y,z
calc2d:
cmp word [offset s_z+si],0
jne gocalc
inc word [offset s_z+si]
gocalc:
mov ax,100;word viewx ;nx:=(viewx*x div (z+cnz)) + cnx;
mov bx,word [offset sn_x+si]
imul bx

;mov bx,word cnz
mov bx,word [offset s_z+si]

idiv bx
add ax,160 ;cnx
mov word calcx,ax ;save calculated 2d value

mov ax,100;word viewy ;ny:=(viewy*y div (z+cnz)) + cny;
mov bx,word [offset sn_y+si]
imul bx

;mov bx,word cnz
mov bx,word [offset s_z+si]

idiv bx
add ax,100 ;cny
mov word calcy,ax ;save calculated 2d value

;set 2d pixel at x,y
mov ax,word calcy ;es:[320*y+x]
mov bx,320
imul bx
add ax,word calcx
mov di,ax         ;save correct offset

cmp word calcx,319  ;no error on display!
ja nix
cmp word calcy,199
ja nix
mov al,byte color
stosb;mov byte es:[di],al

cmp byte key,0 ;no big stars at 0
je nix
cmp byte key,3 ;and 3
je nix
stosb
add di,320-2
stosb
stosb
nix:
ret

;set palette
pal: mov dx,3c8h       ;port for vga palette
     mov al,cl         ;color number in cl; cl is unchanged by call
     out dx,al         ;send color number to port
     inc dx            ;go on because values will be written to port 3c9h
     mov al,byte rval
     out dx,al         ;write red value
     mov al,byte gval
     out dx,al         ;write green value
     mov al,byte bval
     out dx,al         ;write blue value
     inc cl            ;put it here to save some bytes @ the loops
     ret               ;back

;cnx dw 160;{center of screen}
;cny dw 100;{center of screen}
;cnz dw 0;  {nearest point on the screen}

;viewx dw 100;{field width}
;viewy dw 100;{field height}

rval db 255
gval db 255
bval db 0

for_rad dw 180 ;180°

grad dw 0 ;for rotation

key db 0